package org.jvm.classfile.constantinfo;

import org.jvm.classfile.*;

/**
 * author : wangsixiang02
 * date : 2023/1/7
 * desc :
 */
public class ConstantInfoBuilder {

    /**
     * 常量的开头会有一个字节的tag标识常量类型
     */
    public static final int CONSTANT_Class = 7;
    public static final int CONSTANT_Fieldref = 9;
    public static final int CONSTANT_Methodref = 10;
    public static final int CONSTANT_InterfaceMethodref = 11;
    public static final int CONSTANT_String = 8;
    public static final int CONSTANT_Integer = 3;
    public static final int CONSTANT_Float = 4;
    public static final int CONSTANT_Long = 5;
    public static final int CONSTANT_Double = 6;
    public static final int CONSTANT_NameAndType = 12;
    public static final int CONSTANT_Utf8 = 1;
    public static final int CONSTANT_MethodHandle = 15;
    public static final int CONSTANT_MethodType = 16;
    public static final int CONSTANT_InvokeDynamic = 18;


    public static ConstantInfo[] readConstantInfos(ClassReader classReader, ConstantPool constantPool) {
        //注意这里给出的常量池大小实际上比常量池大小要多1，因为常量池索引0的位置是无效索引
        int constantInfoNum = classReader.readUint16();
        ConstantInfo[] constantInfos = new ConstantInfo[constantInfoNum];
        /**
         * 注意0号索引位是无效索引
         */
        for (int i = 1; i < constantInfoNum; i++) {
            constantInfos[i] = newConstantInfo(classReader, constantPool);
            //特殊的，对于Long与Double型常量，占两个索引位
            if (constantInfos[i] instanceof ConstantDoubleInfo
                    || constantInfos[i] instanceof ConstantLongInfo) {
                i++;
            }
        }
        return constantInfos;
    }

    /**
     * 根据头字节的tag判断常量类型，初始化常量
     *
     * @param classReader
     * @param constantPool
     * @return
     */
    private static ConstantInfo newConstantInfo(ClassReader classReader, ConstantPool constantPool) {
        ConstantInfo res;
        int tag = classReader.readUint8();
        switch (tag) {
            case CONSTANT_Integer:
                res = new ConstantIntegerInfo();
                break;
            case CONSTANT_Float:
                res = new ConstantFloatInfo();
                break;
            case CONSTANT_Long:
                res = new ConstantLongInfo();
                break;
            case CONSTANT_Double:
                res = new ConstantDoubleInfo();
                break;
            case CONSTANT_Utf8:
                res = new ConstantUtf8Info();
                break;
            case CONSTANT_String:
                res = new ConstantStringInfo(constantPool);
                break;
            case CONSTANT_Class:
                res = new ConstantClassInfo(constantPool);
                break;
            case CONSTANT_Fieldref:
                res = new ConstantFieldrefInfo(constantPool);
                break;
            case CONSTANT_Methodref:
                res = new ConstantMethodrefInfo(constantPool);
                break;
            case CONSTANT_InterfaceMethodref:
                res = new ConstantInterfaceMethodrefInfo(constantPool);
                break;
            case CONSTANT_NameAndType:
                res = new ConstantNameAndTypeInfo(constantPool);
                break;
            case CONSTANT_MethodType:
                res = new ConstantMethodTypeInfo();
                break;
            case CONSTANT_MethodHandle:
                res = new ConstantMethodHandleInfo();
                break;
            case CONSTANT_InvokeDynamic:
                res = new ConstantInvokeDynamicInfo();
                break;
            default:
                throw new RuntimeException("unknow constant");
        }
        res.readInfo(classReader);
        return res;
    }
}
